package com.honglang.search.lucene;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopFieldCollector;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
import com.google.gson.Gson;
import com.honglang.common.tools.Constants;
import com.honglang.common.tools.FileUtil;
import com.honglang.common.tools.HtmlCodeProcess;
import com.honglang.common.tools.Pager;
import com.honglang.module.article.entity.Article;
import com.honglang.module.article.entity.BlogArticle;

public class SearchEngineer {
	private FileUtil fileUtil;
	private StandardAnalyzer luceneAnalyzer;
	private Gson gson;

	
	
	public SearchEngineer(){
		super();
		initial();
		
	}
	
	public void	 initial(){
		if(null == luceneAnalyzer){
			luceneAnalyzer = new StandardAnalyzer(Version.LUCENE_30);
		}
	}
	
	
	protected boolean initialBeforeCreateIndex(File indexDir,boolean create){
		
		if(create){
			if(!this.getFileUtil().createDirIfNotExist(indexDir)){
				return false;
			}
		}
		return true;
	}
	
	public void createIndexDir(String indexDirPath,List<BlogArticle> list,boolean create){
		//indexDir is the directory that hosts Lucene's index files
		
		File   indexDir = new File(indexDirPath);
        if(!this.initialBeforeCreateIndex(indexDir,create)){
        	return ;
        }
        //dataDir is the directory that hosts the text files that to be indexed
        long startTime = new Date().getTime();
		try {
			IndexWriter indexWriter = new IndexWriter(FSDirectory.open(indexDir), luceneAnalyzer, create, new IndexWriter.MaxFieldLength(12000));
			this.indexDocument(indexWriter, list);
			indexWriter.optimize();
			indexWriter.close();
		} catch (LockObtainFailedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		 long endTime = new Date().getTime();
        System.out.println("It takes " + (endTime - startTime) 
                           + " milliseconds to create index for the files in directory "
        		           );  
	}
	
	
	
	protected void indexDocument(IndexWriter indexWriter,List<BlogArticle> list){
		try {
			for(BlogArticle blogArticle : list){
				Document document = new Document();
				Article article = blogArticle.getArticle();
				document.add(new Field("id", blogArticle.getId().toString(), Field.Store.YES, Field.Index.ANALYZED));
				document.add(new Field("content", HtmlCodeProcess.clearHtmlTag(article.getContent()), Field.Store.YES, Field.Index.ANALYZED));
				document.add(new Field("title", article.getTitle(), Field.Store.NO, Field.Index.ANALYZED));
				document.add(new Field("category", article.getArticleCategories().getCategoryName(), Field.Store.NO, Field.Index.NOT_ANALYZED));
				document.add(new Field("tags",article.returnKeyWordString(), Field.Store.YES, Field.Index.ANALYZED));
				document.add(new NumericField("date").setLongValue(article.getWriteDate().getTime()));
				document.add(new Field(Constants.OBJECT_JSON,gson.toJson(blogArticle),Field.Store.YES, Field.Index.ANALYZED));
				indexWriter.addDocument(document);
			}
		} catch (CorruptIndexException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	public Query bulidQuery(String queryStr,String[] filedString){
		QueryParser parser = new MultiFieldQueryParser(Version.LUCENE_30, filedString, this.getLuceneAnalyzer());
		
		try {
			return parser.parse(queryStr);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			return this.buildQuery(queryStr, filedString[1]);
		}
		
	}
	
	
	public Query buildQuery(String queryStr,String filedString){
		return  new TermQuery(new Term(filedString,queryStr));
	}
	
	
	
	public Sort buildSort(String field,int sortType,boolean reverse){
		Sort sort = new Sort();
		sort.setSort(new SortField(field,sortType,reverse));
		return sort;
	}
	

	public <T> Map<String,Object> search(Query query,Sort sort,String highlightField,String indexDirPath,Class<T> clazz,int pageNo,int pageSize){
		Map<String,Object> map = new HashMap<String,Object>();
		File indexDir = new File(indexDirPath);
        FSDirectory directory;
        Pager pager = new Pager();
		try {
			directory = FSDirectory.open(indexDir);
			IndexSearcher searcher = new IndexSearcher(directory);
			if(!indexDir.exists()){
				pager.CreatePager(pageSize, pageNo, Constants.pageIndexCountPerPage, 0, new ArrayList<T>());
				map.put("highlightList", new ArrayList<String>());
			}
			TopFieldCollector collector = TopFieldCollector.create(sort, pageNo*pageSize, false, false, false, false);
	        searcher.search(query,collector);
	        ScoreDoc[] hits = collector.topDocs().scoreDocs;
	        int startIndex = pageSize*(pageNo-1);
	        int endIndex = startIndex + pageSize  ;
	        endIndex = endIndex  > hits.length ? hits.length : endIndex;
	        List<T> list = new ArrayList<T>();
	        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");    
      	  	Highlighter highlighter = new Highlighter(simpleHTMLFormatter,new QueryScorer(query));
      	  	List<String> highlightList = new ArrayList<String>();
      	  	for(int i = startIndex; i < endIndex; i++){
	        	Document document = searcher.doc(hits[i].doc);
	        	list.add(gson.fromJson(document.get(Constants.OBJECT_JSON), clazz));
	            String highLightText = highlighter.getBestFragment(this.getLuceneAnalyzer(),highlightField,document.get(highlightField));
	            highlightList.add(highLightText.replace("<script", "&lt;script").replace("</script", "&lt;/script"));
	        }  
      	    pager.CreatePager(pageSize, pageNo, Constants.pageIndexCountPerPage, collector.getTotalHits(), list);
      	    map.put("highlightList", highlightList);
        } catch (IOException e) {
        	pager.CreatePager(pageSize, pageNo, Constants.pageIndexCountPerPage, 0, new ArrayList<T>());
        	map.put("highlightList", new ArrayList<String>());
		} catch (InvalidTokenOffsetsException e) {
        	pager.CreatePager(pageSize, pageNo, Constants.pageIndexCountPerPage, 0, new ArrayList<T>());
        	map.put("highlightList", new ArrayList<String>());
		}
		map.put("pager", pager);
		return map;
	}
	
	
	public <T> Pager search(Query query,Sort sort,String indexDirPath,Class<T> clazz,int pageNo,int pageSize){
		File indexDir = new File(indexDirPath);
        FSDirectory directory;
        Pager pager = new Pager();
		try {
			directory = FSDirectory.open(indexDir);
			IndexSearcher searcher = new IndexSearcher(directory);
			if(!indexDir.exists()){
				pager.CreatePager(pageSize, pageNo, Constants.pageIndexCountPerPage, 0, new ArrayList<T>());
			}
			TopFieldCollector collector = TopFieldCollector.create(sort, pageNo*pageSize, false, false, false, false);
	        searcher.search(query,collector);
	        ScoreDoc[] hits = collector.topDocs().scoreDocs;
	        int startIndex = pageSize*(pageNo-1);
	        int endIndex = startIndex + pageSize  ;
	        endIndex = endIndex  > hits.length ? hits.length : endIndex;
	        List<T> list = new ArrayList<T>();
      	  	for(int i = startIndex; i < endIndex; i++){
	        	Document document = searcher.doc(hits[i].doc);
	        	list.add(gson.fromJson(document.get(Constants.OBJECT_JSON), clazz));
	        }    
      	  	pager.CreatePager(pageSize, pageNo, Constants.pageIndexCountPerPage, collector.getTotalHits(), list);
        } catch (IOException e) {
        	pager.CreatePager(pageSize, pageNo, Constants.pageIndexCountPerPage, 0, new ArrayList<T>());
		}
		return pager;
	}
	
	
	public FileUtil getFileUtil() {
		return fileUtil;
	}

	public void setFileUtil(FileUtil fileUtil) {
		this.fileUtil = fileUtil;
	}
	
	
	public StandardAnalyzer getLuceneAnalyzer() {
		return luceneAnalyzer;
	}

	public void setLuceneAnalyzer(StandardAnalyzer luceneAnalyzer) {
		this.luceneAnalyzer = luceneAnalyzer;
	}

	public Gson getGson() {
		return gson;
	}

	public void setGson(Gson gson) {
		this.gson = gson;
	}
	
	
	public static void main(String[] args){
		
		//ApplicationContext ac = new FileSystemXmlApplicationContext("E:/honglang/workspace/WolfBlog/WebContent/WEB-INF/applicationContext-common.xml");   
		//SearchEngineer se = (SearchEngineer) ac.getBean("searchEngineer");
		//List<BlogArticle> list =  se.search("的", "E://honglang//workspace//.metadata//.plugins//org.eclipse.wst.server.core//tmp0//wtpwebapps//WolfBlog//search//index",BlogArticle.class);
	   //	System.out.println((list.get(0)).getArticle().getContent());
		System.out.println("End");
	}
	
}
